/* 
 * Author: vdaras
 */

#include "Container.h"

#include "../MouseEvent.h"
#include "../Utilities.h"
#include "Exception.h"
#include <algorithm>
#include <iostream>

namespace gui
{

/********************************Functors used by this class***************************************/
    
class NameComparison : public std::unary_function< Component*, bool >
{
private:
    std::string m_name;
    
public:
    NameComparison(const std::string& name)
    :
    m_name(name)
    {
    }
    
    bool operator()(Component *arg)
    {
	return arg->GetName( ) == m_name;
    }
};

/**************************************************************************************************/
    

/**
 * Constructor initializes the parent class of the Container, its position
 * and dimension.
 *
 * @param parent: Container's parent Component in the Composite tree
 * @param name: Container's name
 * @param w: Container's width
 * @param h: Container's height
 */

Container::Container(const std::string &name, int x, int y, int w, int h)
:
Component(name, w, h)
{
    SetPosition( x, y );
}


/**
 * Constructor initializes the parent class of the Container, its position
 * and dimension. It also makes this Container a part of another Container.
 *
 * @param parent: Container's parent Component in the Composite tree
 * @param name: Container's name
 * @param w: Container's width
 * @param h: Container's height
 */

Container::Container(const std::string &name, Container *parent, int x, int y, int w, int h)
:
Component(name, parent, x, y, w, h)
{
}


/**
 * Destructor frees all contained Components.
 */

Container::~Container()
{
    Clear( );
}


/**
 * Resizes this Container.
 *
 * @param width: new width.
 * @param height: new height.
 */

void Container::Resize(int width, int height)
{
    SetWidth( width );
    
    SetHeight( height );    
}


/**
 * This routine adds a component to the container's list. IMPORTANT, the container
 * now retains ownership of the component, thus is responsible for the component's
 * memory management.
 *
 * @param toAdd: the component to add
 */

void Container::AddComponent(Component* toAdd)
{
    AddComponent( toAdd, 0, 0 );
}


/**
 * This routine adds a component to the container's list. It also places
 * the component on the provided coordinates inside the container. IMPORTANT, 
 * the container now retains ownership of the component, thus is responsible for
 * the component's memory management.
 *
 * @param toAdd: the component to add
 * @param x: desired x in container
 * @param y: desired y in container
 */

void Container::AddComponent(Component* toAdd, int x, int y)
{
    //set this container as the component's parent
    toAdd->SetParent( this );

    //change component's position to the desired one
    toAdd->SetPosition( x, y );
    
    //now we must determine the visible part of the component inside its container.
    toAdd->UpdateVisiblePart();
    
    //append component to the component's list
    m_components.push_front( toAdd );
}


/**
 * This routine brings the specified Component to the front.
 *
 * @param comp: component to bring to the front
 */

void Container::BringToFront(Component* comp)
{
    std::list< Component* >::iterator end( m_components.end( ) );
    
    std::list< Component* >::iterator compIter(
					       std::find(
							 m_components.begin( ),
							 m_components.end( ),
							 comp
							)
					      );	  
    if( compIter != end )
    {
	Component* element = *compIter;
	
	m_components.erase( compIter );
	
	m_components.push_front( element );
    }
}


/**
 * This routine removes a component from the Container's list. IMPORTANT, the client
 * code is responsible for the component's memory management now!
 *
 * @param toRemove: the component to Remove
 */

void Container::RemoveComponent(Component* toRemove)
{
    if( toRemove->GetParent() == this )
    {
	//remove Component's parent
	toRemove->SetParent( NULL );
	//remove Component from the components list
	m_components.remove( toRemove );
    }
}


/**
 * This routine finds a component in the current container based on it's name
 * identifier. Returns null if it doesn't exist.
 *
 * @param name: the name of the component we wish to search for
 */

Component *Container::FindComponent(const std::string& name)
{
    std::list< Component* >::iterator end( m_components.end( ) );
    
    std::list< Component* >::iterator result( 
                                             std::find_if( 
                                                          m_components.begin( ),
							  end,
							  NameComparison( name )
							 )
					    );
					     
    return result != end ? (*result) : NULL;
}


/**
 * This routine draws each component in the component's list
 *
 * @param target: surface to draw the components on.
 */

void Container::Draw(sdl::Surface* target) const
{
    //components are sorted in display order, so we must draw them in reverse
    
    std::list< Component* >::const_reverse_iterator iter( m_components.rbegin( ) ), end( m_components.rend( ) );
    
    for( ; iter != end; ++iter )
    {
	(*iter)->Draw( target );
    }
}


/**
 * Forwards the mouse event to all its components.
 *
 * @param event: a mouse event occurred.
 */

void Container::OnMouseEvent(MouseEvent& event)
{
    std::list< Component* >::iterator iter( m_components.begin( ) ), end( m_components.end( ) );
    
    for( ; iter != end; ++iter )
    {
	(*iter)->OnMouseEvent( event );
    }
}



/**
 * Returns a pointer to this Container object. Used to provide run time information
 * on the Composite structure. See Composite pattern.
 */

Container *Container::GetContainer()
{
    return this;
}


/**
 * Clears all Components belonging to this Container. 
 */

void Container::Clear()
{
    //for each component in the contained components list
    std::list< Component* >::iterator begin( m_components.begin( ) ), end( m_components.end( ) );

    std::for_each( begin, end, FreePointer< Component > ( ) );
}


};
